﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Net;
using System.Threading;
using System.Xml;
using System.Xml.Xsl;

namespace GPSTracker2
{
    public partial class frmMain : Form
    {
        public static string link;
        public static int duration;
        public static int interval;
        public static int count;
        public static WebRequest request;
        public static HttpWebResponse response;
        public static Stream dataStream;
        public static StreamReader reader;
        public static StreamWriter file;

        public frmMain()
        {
            InitializeComponent();
        }

        private void fdlgOpen_FileOk(object sender, CancelEventArgs e)
        {
            if (fdlgOpen.FileName.Length != 0)
            {
                txtSettings.Text = fdlgOpen.FileName;
                StreamReader file = new StreamReader(fdlgOpen.FileName);
                string temp = file.ReadLine();
                if (!temp.Contains("Profile:"))
                {
                    MessageBox.Show("Not a valid GPSTracker file!", "GPSTracker", MessageBoxButtons.OK);
                    file.Close();
                    return;
                }
                string[] temp2 = temp.Split(new Char[] { ':' }, 2);
                txtName.Text = temp2[1];
                
                temp = file.ReadLine();
                temp2 = temp.Split(new Char[] { ':' }, 2);
                link = temp2[1];
                file.Close();
            }
            grpMain.Enabled = true;
        }

        private void btnLoad_Click(object sender, EventArgs e)
        {
            fdlgOpen.ShowDialog();
        }

        private void btnSFile_Click(object sender, EventArgs e)
        {
            fdlgSave.ShowDialog();
        }

        private void btnSSave_Click(object sender, EventArgs e)
        {
            if (txtSName.Text.Length == 0)
            {
                MessageBox.Show("Please Enter a Profile Name.", "GPSTracker", MessageBoxButtons.OK);
                return;
            }
            if (txtSLink.Text.Length == 0)
            {
                MessageBox.Show("Please Enter a JSON Link.", "GPSTracker", MessageBoxButtons.OK);
                return;
            }
            if (fdlgSave.FileName.Length == 0)
            {
                MessageBox.Show("Please Enter a JSON Link.", "GPSTracker", MessageBoxButtons.OK);
                return;
            }
            StreamWriter file = new StreamWriter(fdlgSave.FileName);
            file.WriteLine(String.Format("Profile:{0}", txtSName.Text));
            file.WriteLine(String.Format("Link:{0}", txtSLink.Text));
            file.Close();
            MessageBox.Show("File Saved.", "GPSTracker", MessageBoxButtons.OK);
            cleanupSettings();
            tctrlMain.SelectedIndex = 0;
        }

        private void cleanupSettings()
        {
            txtSName.Text = "";
            txtSLink.Text = "";
            fdlgSave.FileName = "";
            lblTrack.Visible = false;
        }

        private void btnReset_Click(object sender, EventArgs e)
        {
            txtSettings.Text = "Load Settings File...";
            txtName.Text = "";
            grpMain.Enabled = false;
            fdlgOpen.FileName = "";
            lblTrack.Visible = false;
            fdlgSave2.FileName = "";
        }

        private void btnTrack_Click(object sender, EventArgs e)
        {
            if (fdlgSave2.FileName.Length == 0)
            {
                MessageBox.Show("Select output file!", "GPSTracker", MessageBoxButtons.OK);
                return;
            }
            lblTrack.Visible = true;
            grpMain.Enabled = false;
            btnLoad.Enabled = false;
            
            interval = Convert.ToInt16(txtMins.Text);
            duration = Convert.ToInt16(txtHours.Text);
            count = 0;

            file = new StreamWriter(fdlgSave2.FileName);
            file.WriteLine("# GPSTraker Application using Google Latitude");
            file.WriteLine(String.Format("# Start tracking for {0} hours...", duration));
            file.WriteLine("# Time,Lon,Lat,Accuracy(m),UpdateSince(min)");
            file.Close();

            timer.Interval = 1000 * 60 * interval;
            timer.Start();
        }

        private void btnSave_Click(object sender, EventArgs e)
        {
            fdlgSave2.ShowDialog();
        }

        private void timer1_Tick(object sender, System.EventArgs e)
        {
            if (count < Convert.ToInt16((60 / interval * duration)))
            {
                file = new StreamWriter(fdlgSave2.FileName, true);
                string latString = getLatitudeString(link);
                DateTime updateTime = Convert.ToDateTime(getTime(latString));
                file.WriteLine(String.Format("{0},{1},{2},{3}", string.Format("{0:MM/dd/yyyy HH:mm:ss}", updateTime.AddHours(-6)), getLocation(latString), getAccuracy(latString), (DateTime.Now.ToUniversalTime() - updateTime).TotalMinutes));
                file.Close();
                count++;
                lblTrack.Text = String.Format("{0} location(s) tracked.. Close anytime to exit..", count);
                Update();
            }
            else
            {
                timer.Stop();
                count = 0;
                grpMain.Enabled = true;
                lblTrack.Visible = false;
                lblTrack.Text = "Tracking in progress... Close to exit at anytime...";
                btnLoad.Enabled = true;
                MessageBox.Show("Completed!", "GPSTracker", MessageBoxButtons.OK);
            }
        }

        private static string getLatitudeString(string link)
        {
            request = WebRequest.Create(link);
            response = (HttpWebResponse)request.GetResponse();
            if (response.StatusDescription == "OK")
            {
                dataStream = response.GetResponseStream();
                reader = new StreamReader(dataStream);
                string temp = reader.ReadToEnd();
                reader.Close();
                dataStream.Close();
                response.Close();
                return temp;
            }
            else
            {
                return "Error in retrieving from Google Latitude!";
            }
        }

        private static string getTime(string latString)
        {
            string[] temp;
            if (latString.Contains("\"timeStamp\": "))
                temp = latString.Split(new string[] { "\"timeStamp\": " }, StringSplitOptions.RemoveEmptyEntries);
            else
                return "-1";
            temp = temp[1].Split(new Char[] { ',' }, 2);
            DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0);
            return (origin.AddSeconds(Convert.ToDouble(temp[0]))).ToString();
        }

        private static string getLocation(string latString)
        {
            string[] temp;
            if (latString.Contains("\"coordinates\": "))
                temp = latString.Split(new string[] { "\"coordinates\": " }, StringSplitOptions.RemoveEmptyEntries);
            else
                return "[-1, -1]";
            temp = temp[1].Split(new Char[] { '}' }, 2);
            temp[0] = temp[0].Replace("[", "");
            temp[0] = temp[0].Replace("]", "");
            temp[0] = temp[0].Replace(" ", "");
            return temp[0];
        }

        private static string getAccuracy(string latString)
        {
            string[] temp;
            if (latString.Contains("\"accuracyInMeters\": "))
                temp = latString.Split(new string[] { "\"accuracyInMeters\": " }, StringSplitOptions.RemoveEmptyEntries);
            else
                return "-1";
            temp = temp[1].Split(new Char[] { ',' }, 2);
            return temp[0];
        }

        void txtHours_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)
        {
            if (!char.IsNumber(e.KeyChar) & (Keys)e.KeyChar != Keys.Back & e.KeyChar != '.')
            {
                e.Handled = true;
            }
            else
                e.Handled = false;
        }

        void txtMins_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)
        {
            if (!char.IsNumber(e.KeyChar) & (Keys)e.KeyChar != Keys.Back & e.KeyChar != '.')
            {
                e.Handled = true;
            }
            else
                e.Handled = false;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            fdlgKMLOpen.ShowDialog();
        }

        private void btnKMLSave_Click(object sender, EventArgs e)
        {
            fdlgKMLSave.ShowDialog();
        }

        private void btnKMLGenerate_Click(object sender, EventArgs e)
        {
            if (fdlgKMLOpen.FileName.Length == 0)
            {
                MessageBox.Show("Please select GPS locations tracked file!","GPSTracker",MessageBoxButtons.OK);
                return;
            }
            if (fdlgKMLSave.FileName.Length == 0)
            {
                MessageBox.Show("Please select a KML file to save!", "GPSTracker", MessageBoxButtons.OK);
                return;
            }
            
            // Open the file
            StreamReader input = new StreamReader(fdlgKMLOpen.OpenFile());
            input.ReadLine(); input.ReadLine(); input.ReadLine();

            // Prepare the kml file
            StreamWriter output = new StreamWriter(fdlgKMLSave.FileName);
            output.WriteLine("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
            output.WriteLine("<kml xmlns=\"http://www.opengis.net/kml/2.2\">");
            output.WriteLine("<Document>");
            output.WriteLine("  <name>GPSTracker</name>");
            output.WriteLine("  <Folder>");
            output.WriteLine("    <name>GPSTracker</name>");

            string text;
            string lat = "";
            string lon = "";
            int counter = 0;
            string lastUpdate = "0";
            text = input.ReadLine();
            string[] values = text.Split(new char[] { ',' }, StringSplitOptions.None);
            
            // Get HTML from link
            DateTime accessTime = new DateTime();
            accessTime = Convert.ToDateTime(values[0]);
            double[,] emissions = getEmissions(accessTime);
            //lastUpdate = values[4];

            // Loop through each line and get the pollutant
            while (text != null)
            {
                values = text.Split(new char[] { ',' }, StringSplitOptions.None);
                if (lat != values[2] || lon != values[1])
                {
                    lat = values[2];
                    lon = values[1];
                    counter++;
                    output.WriteLine(string.Format("    <Placemark>"));
                    output.WriteLine(string.Format("      <name>{0}</name>", counter));
                    output.WriteLine(string.Format("      <description>Time : {0} \n Position Accuracy : {1} m \n PM25 : {2} \n Time since last update : {3} minutes</description>", values[0], values[3], getPM25(emissions, Convert.ToDateTime(values[0]), Convert.ToDouble(lat), Convert.ToDouble(lon)), lastUpdate));
                    output.WriteLine(string.Format("      <Point>"));
                    output.WriteLine(string.Format("        <coordinates>{0}, {1}</coordinates>", lon, lat));
                    output.WriteLine(string.Format("      </Point>"));
                    output.WriteLine(string.Format("    </Placemark>"));
                }
                lastUpdate = values[4];                
                text = input.ReadLine();
            }
            output.WriteLine("  </Folder>");
            output.WriteLine("</Document>");
            output.WriteLine("</kml>");
            

            input.Close();
            output.Close();
            MessageBox.Show("KML File Generated!", "GPSTracker", MessageBoxButtons.OK);
        }

        private double[,] getEmissions(DateTime accessDate)
        {
            // Link
            WebRequest request;
            HttpWebResponse response;
            Stream dataStream;
            StreamReader reader;

            // Return Variable
            double[,] retVal = new double[6, 26];
            // Set Lat Long
            retVal[0, 0] = 44.7359003; retVal[0, 1] = -93.2388414;
            retVal[1, 0] = 44.9603863; retVal[1, 1] = -93.0354267;
            retVal[2, 0] = 44.9527919; retVal[2, 1] = -93.2600676;
            retVal[3, 0] = 44.9011884; retVal[3, 1] = -93.2386937;
            retVal[4, 0] = 45.2101109; retVal[4, 1] = -93.6629035;
            retVal[5, 0] = 45.1244936; retVal[5, 1] = -93.2184774;

            request = WebRequest.Create("http://aqi.pca.state.mn.us/hourly/final.cfm?region=Twin+Cities&poll=BAMR24H&theDate=" + (accessDate.Date.ToString().Split(new Char[] { ' ' }, 3))[0]);
            response = (HttpWebResponse)request.GetResponse();
            if (response.StatusDescription == "OK")
            {
                dataStream = response.GetResponseStream();
                reader = new StreamReader(dataStream);
                string [] split = reader.ReadToEnd().Split(new string[] { "<tr><td>0</td>" }, StringSplitOptions.RemoveEmptyEntries);
                split = split[1].Split(new string[] { "</table>" }, StringSplitOptions.RemoveEmptyEntries);
                reader.Close();
                dataStream.Close();
                response.Close();
                
                string values = "<table><tr><td>0</td>" + split[0] + "</tr></table>";
                split = null;
                values = values.Replace("<font size=\"-2\">", ""); values = values.Replace("</font>", "");
                values = values.Replace("\t", "");
                values = values.Replace(" ", "");
                values = values.Replace("\n", "");

                values = values.Replace("<tr><td>0</td>", "");
                values = values.Replace("<tr><td>1</td>", "");
                values = values.Replace("<tr><td>2</td>", "");
                values = values.Replace("<tr><td>3</td>", "");
                values = values.Replace("<tr><td>4</td>", "");
                values = values.Replace("<tr><td>5</td>", "");
                values = values.Replace("<tr><td>6</td>", "");
                values = values.Replace("<tr><td>7</td>", "");
                values = values.Replace("<tr><td>8</td>", "");
                values = values.Replace("<tr><td>9</td>", "");
                values = values.Replace("<tr><td>10</td>", "");
                values = values.Replace("<tr><td>11</td>", "");
                values = values.Replace("<tr><td>12</td>", "");
                values = values.Replace("<tr><td>13</td>", "");
                values = values.Replace("<tr><td>14</td>", "");
                values = values.Replace("<tr><td>15</td>", "");
                values = values.Replace("<tr><td>16</td>", "");
                values = values.Replace("<tr><td>17</td>", "");
                values = values.Replace("<tr><td>18</td>", "");
                values = values.Replace("<tr><td>19</td>", "");
                values = values.Replace("<tr><td>20</td>", "");
                values = values.Replace("<tr><td>21</td>", "");
                values = values.Replace("<tr><td>22</td>", "");
                values = values.Replace("<tr><td>23</td>", "");
                 
                values = values.Replace("<tr>", ""); values = values.Replace("</tr>", "");
                values = values.Replace("<table>", ""); values = values.Replace("</table>", "");
                values = values.Replace("Invalid", "-1");
                values = values.Replace("</td><td>", ",");
                values = values.Replace("<td>", ""); values = values.Replace("</td>", "");
                Clipboard.SetText(values);
                split = values.Split(new char[] { ',' }, StringSplitOptions.None);

                // PM 2.5 values
                int idx = 0;
                for (int i = 2; i < 26; i++)
                    for (int j = 0; j < 6; j++)
                    {
                        retVal[j, i] = Convert.ToDouble(split[idx]);
                        idx++;
                    }
            }
            else
            {
                return new double[1,1]{{-1}};
            }

            return retVal;
        }

        private string getPM25(double [,] emissions, DateTime accessTime, double lat, double lon)
        {
            // Restriction data should be in one day
            // Get distances and nearest monitor
            double[,] dist = new double[6, 1];
            int minIdx = -1;
            double minDist = 360;

            for (int i = 0; i < 6; i++)
            {
                dist[i, 0] = Math.Sqrt(((lat - emissions[i, 0]) * (lat - emissions[i, 0])) + 
                    ((lon - emissions[i, 1]) * (lon - emissions[i, 1])));
                if (minDist > dist[i, 0])
                {
                    minDist = dist[i, 0];
                    minIdx = i;
                }
            }

            // Get Nearest Hour
            double time = Convert.ToDouble(string.Format("{0:HH}", accessTime)) + Convert.ToDouble(accessTime.Minute) / 60;
            int timeIdx = Convert.ToInt16(Math.Round(time) + 2);
            // Falls in the next day!!
            if (timeIdx == 26)
                timeIdx--;
            // Get PM2.5
            return string.Format("{0} \n MonitorID : {1}",emissions[minIdx,timeIdx], minIdx+1);
        }

        private void fdlgKMLOpen_FileOk(object sender, CancelEventArgs e)
        {
           fdlgKMLSave.FileName = fdlgKMLOpen.FileName.Substring(0,fdlgKMLOpen.FileName.Length-4) + ".kml";
        }

    }
}
